/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package lesson10;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author netcracker
 */
public class LinkedListVector implements IVector, Observable {

    private ListElement head;
    private ArrayList<Observer> observers;
    private boolean edited = false;

    public LinkedListVector() {
        head = new ListElement(null, null, null);
        observers = new ArrayList<Observer>();
    }

    @Override
    public void fill(IVector llv) throws IncompatibleVectorSizesException {
        if (this.getLength() == llv.getLength()) {
            ListElement current = head;
            for (int i = 0; i < this.getLength(); i++) {
                current.value = llv.get(i);
                current = current.next;
            }
            notifyToObservers();
        } else {
            throw new IncompatibleVectorSizesException();
        }
    }

    @Override
    public void fill(double[] d) throws IncompatibleVectorSizesException {
        if (this.getLength() == d.length) {
            ListElement current = this.head;
            while (this.getLength() < d.length) {
                this.add(0);
            }
            for (int i = 0; i < d.length; i++) {
                current.value = d[i];
                current = current.next;
            }
            notifyToObservers();
        } else {
            throw new IncompatibleVectorSizesException();
        }

    }

    @Override
    public void set(double d, int index) {
        try {
            ListElement current = head;
            for (int j = 0; j < index; j++) {
                current = current.next;
            }
            current.value = d;
            notifyToObservers(index, d);
        } catch (IndexOutOfBoundsException e) {
            throw new VectorIndexOutOfBoundsException();
        }
    }

    @Override
    public void add(double o) {
        ListElement current = head;
        if (head.value != null) {
            while (current.next != null) {
                current = current.next;
            }
            current.next = new ListElement(o, current, null);
            head.previous = current.next;
        } else {
            head.value = o;
        }

        notifyToObservers(this.getLength(), o);

    }

    @Override
    public void delete(int index) {
        ListElement current = head;
        ListElement nextBuffer;
        ListElement previousBuffer;

        try {
            for (int j = 0; j < index; j++) {
                current = current.next;
            }

            previousBuffer = current.previous;
            nextBuffer = current.next;
            previousBuffer.next = nextBuffer;
            nextBuffer.previous = previousBuffer;
            notifyToObservers();
        } catch (IndexOutOfBoundsException e) {
            throw new VectorIndexOutOfBoundsException();
        }
    }

    @Override
    public Double get(int index) {
        ListElement current = head;
        try {
            if (index != 0) {
                for (int j = 0; j < index; j++) {
                    current = current.next;
                }
                return current.value;
            } else {
                return current.value;
            }
        } catch (IndexOutOfBoundsException e) {
            throw new VectorIndexOutOfBoundsException();
        }

    }

    @Override
    public int getLength() {
        if (head.value == null) {
            return 0;
        } else {
            int i = 1;
            ListElement current = head;
            while (current.next != null) {
                ++i;
                current = current.next;
            }
            return i;
        }
    }

    @Override
    public void multiply(double d) {
        ListElement current = this.head;
        for (int i = 0; i < this.getLength(); i++) {
            current.value = current.value * d;
            current = current.next;
        }
        notifyToObservers();
    }

    @Override
    public void plus(IVector llv) throws IncompatibleVectorSizesException {
        ListElement current1 = this.head;
        if (this.getLength() == llv.getLength()) {
            for (int i = 0; i < this.getLength(); i++) {
                double current2 = llv.get(i);

                current1.value = current1.value + current2;
                current1 = current1.next;
            }
            notifyToObservers();
        } else {
            throw new IncompatibleVectorSizesException();
        }
    }

    @Override
    public boolean isSame(IVector llv) throws IncompatibleVectorSizesException {
        if (this.getLength() == llv.getLength()) {


            for (int i = 0; i < this.getLength(); i++) {

                boolean b = this.get(i) == llv.get(i);
                if (b == false) {
                    return false;
                } else {
                    return true;
                }
            }

        } else {
            throw new IncompatibleVectorSizesException();
        }
        return false;
    }

    @Override
    public void sort() {
        for (int i = this.getLength() - 1; i > 0; i--) {
            for (int j = 0; j < i; j++) {
                if (this.get(j) > this.get(j + 1)) {
                    this.swap(j, j + 1);
                }
            }
        }

        notifyToObservers();
    }

    @Override
    public void swap(int i, int j) {
        double t = this.get(i);
        ListElement current = this.head;
        for (int k = 0; k < i; k++) {
            current = current.next;
        }
        current.value = this.get(j);
        current = this.head;
        for (int k = 0; k < j; k++) {
            current = current.next;
        }
        current.value = t;
    }

    @Override
    public Iterator<Double> iterator() {
        return new Iterator<Double>() {
            int marker = 0;

            @Override
            public boolean hasNext() {
                if (getLength() > marker) {
                    return true;
                }
                return false;
            }

            @Override
            public Double next() {
                double result = get(marker);
                marker++;

                return result;

            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
            }
        };
    }

    class ListElement implements Cloneable, Serializable {

        Double value;
        ListElement previous;
        ListElement next;

        public ListElement() {
        }

        public ListElement(Double value, ListElement previous, ListElement next) {
            this.value = value;
            this.previous = previous;
            this.next = next;
        }

        @Override
        public ListElement clone() throws CloneNotSupportedException {
            ListElement result;
            result = (ListElement) super.clone();
            return result;

        }
    }

    @Override
    public String toString() {
        StringBuffer stringBuffer = new StringBuffer(this.getClass().toString());
        stringBuffer.append("\n" + "Contains:" + "\n");
        for (int i = 0; i < this.getLength(); i++) {
            stringBuffer.append(this.get(i)).append("\n");
        }
        return stringBuffer.toString();
    }

    @Override
    public LinkedListVector clone() throws CloneNotSupportedException {
        LinkedListVector result;
        result = (LinkedListVector) super.clone();
        result.head = new ListElement();
        for (int i = 0; i < this.getLength(); i++) {
            result.add(this.get(i));
        }
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        obj = (IVector) obj;
        if (obj == null) {
            return false;
        }

        if (!(obj instanceof IVector)) {
            return false;
        }
        final IVector other = (IVector) obj;

        if (this.getLength() != other.getLength()) {
            return false;
        }
        if (this.hashCode() != obj.hashCode()) {
            return false;
        }
        try {
            if (!this.isSame(other)) {
                return false;
            }
        } catch (IncompatibleVectorSizesException ex) {
            Logger.getLogger(LinkedListVector.class.getName()).log(Level.SEVERE, null, ex);
        }

        return true;
    }

    @Override
    public int hashCode() {
        long result = Double.doubleToLongBits(this.get(0));
        int ups;
        int downs;

        for (int i = 1; i < this.getLength() - 1; i++) {
            result = result ^ Double.doubleToLongBits(this.get(i));
        }
        downs = (int) result;
        result = result >> 32;
        ups = (int) result;
        return downs ^ ups;
    }

    @Override
    public void addObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyToObservers() {
        for (Observer observer : observers) {
            observer.updated();
        }
    }

    @Override
    public void notifyToObservers(int index, double value) {
        for (Observer observer : observers) {
            observer.updated(index, value);
        }
    }

    @Override
    public boolean isEdited() {
        return edited;
    }

    @Override
    public void setEdited(boolean b) {
        edited = b;
    }
}